Skip to content

Add pre-operational self tests for UNO firmware - #511

Open
msft-nathwania wants to merge 19 commits into
Azure:mainfrom
msft-nathwania:user/v-nathwania/uno_selftest
Open

Add pre-operational self tests for UNO firmware#511
msft-nathwania wants to merge 19 commits into
Azure:mainfrom
msft-nathwania:user/v-nathwania/uno_selftest

Conversation

@msft-nathwania

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings July 2, 2026 23:32
@msft-nathwania

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree company="Microsoft"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces pre-operational Cryptographic Algorithm Self-Tests (CAST) for the Uno firmware, gating boot completion on known-answer test (KAT) success, while also extending the IO SRAM layout to reserve a dedicated IO slot for self-test buffers and tightening memory-ordering around AES/SHA/PKA command submission.

Changes:

  • Reserve a dedicated GSRAM IO buffer slot for self-tests (CAST) and adjust related constants/overlays.
  • Add a pre-op CAST suite (AES-CBC + per-PKA-engine RSA mod-exp; HKDF/KBKDF intended) and run it during boot before transitioning to Running.
  • Add explicit dmb() barriers before AES/SHA/UPKA doorbell writes to ensure descriptor write ordering on Cortex-M7.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
fw/plat/uno/rdl/soc/gsram_map.rdl Expands SRAM_IO_BUF to 34 slots and documents the dedicated self-test slot.
fw/plat/uno/rdl/soc/dtcm_map.rdl Documents why DTCM scratch remains at 33 slots (host+admin only).
fw/plat/uno/fw/reg/soc/src/io_gsram.rs Updates generated constants/overlay sizing for 34 SRAM_IO_BUF slots.
fw/plat/uno/fw/pal/src/self_test/vectors.rs Adds fixed KAT vectors (AES-CBC, HKDF, KBKDF, RSA mod-exp).
fw/plat/uno/fw/pal/src/self_test/aes_cbc.rs Implements AES-256-CBC CAST using the PAL AES path.
fw/plat/uno/fw/pal/src/self_test/pka.rs Implements per-engine RSA-2048 mod-exp CAST using pinned UPKA engines.
fw/plat/uno/fw/pal/src/self_test/mod.rs Defines the pre-op self-test suite entrypoint and module layout.
fw/plat/uno/fw/pal/src/pal.rs Runs CAST during boot and adds polling-based wakeups for AES/SHA.
fw/plat/uno/fw/pal/src/lib.rs Wires the self_test module into the Uno PAL crate.
fw/plat/uno/fw/pal/src/io.rs Adds a dedicated UnoHsmIo::self_test() handle for the reserved slot.
fw/plat/uno/fw/pal/src/alloc.rs Reassigns admin slot index and introduces a dedicated self-test slot index.
fw/plat/uno/fw/drivers/upka/src/executor.rs Adds dmb() before UPKA doorbell submission for correct ordering.
fw/plat/uno/fw/drivers/upka/Cargo.toml Adds cortex-m dependency for dmb().
fw/plat/uno/fw/drivers/sha/src/api.rs Adds dmb() before SHA doorbell submission and updates related comments.
fw/plat/uno/fw/drivers/sha/Cargo.toml Adds cortex-m dependency for dmb().
fw/plat/uno/fw/drivers/aes/src/api.rs Adds dmb() before AES doorbell submission.
fw/plat/uno/fw/drivers/aes/Cargo.toml Adds cortex-m dependency for dmb().
fw/pal/traits/src/error.rs Adds HsmError::SelfTestKatMismatch for CAST failures.

Comment thread fw/plat/uno/fw/pal/src/self_test/mod.rs
Comment thread fw/plat/uno/fw/pal/src/self_test/mod.rs
Comment thread fw/plat/uno/fw/pal/src/alloc.rs
Copilot AI review requested due to automatic review settings July 2, 2026 23:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

fw/plat/uno/fw/pal/src/io.rs:111

  • UnoHsmIo::self_test() sets index = SELF_TEST_IO_INDEX (33), but IO_META is only 33 entries (0..=32). Any call to pid() / queue_id() / queue_idx() on a self-test IO would index IO_Q.io_meta[33] out of bounds (undefined behavior). Even if the current self-test path avoids these methods, it’s easy for shared code (logging, future refactors) to call them.

Consider either (a) special-casing pid/queue_* for the self-test IO to return fixed defaults without touching IO_META, or (b) making io_meta() map the self-test slot to a safe metadata entry.

    /// Returns a reference to the IO_META entry for this slot.
    #[inline]
    fn io_meta(&self) -> &IoMetaEntry {
        &IO_Q.io_meta[self.index as usize]
    }

Comment thread fw/plat/uno/fw/pal/src/alloc.rs
Copilot AI review requested due to automatic review settings July 11, 2026 00:19
@msft-nathwania
msft-nathwania force-pushed the user/v-nathwania/uno_selftest branch from be2d534 to 4fc2fc2 Compare July 11, 2026 00:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Comment thread fw/plat/uno/fw/drivers/rng/src/api.rs
Comment thread fw/plat/uno/fw/pal/src/self_test/vectors.rs Outdated
Copilot AI review requested due to automatic review settings July 11, 2026 00:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Comment thread fw/plat/uno/fw/drivers/rng/src/api.rs
Comment thread fw/plat/uno/fw/drivers/upka/src/engine.rs
Copilot AI review requested due to automatic review settings July 11, 2026 00:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Comment thread fw/plat/uno/fw/pal/src/io.rs
Comment thread fw/plat/uno/fw/pal/src/pal.rs
Copilot AI review requested due to automatic review settings July 13, 2026 19:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Comment thread fw/plat/uno/fw/pal/src/alloc.rs
Comment thread fw/plat/uno/fw/drivers/rng/src/api.rs
Copilot AI review requested due to automatic review settings July 13, 2026 19:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Comment thread fw/plat/uno/fw/pal/src/io.rs Outdated
Copilot AI review requested due to automatic review settings July 13, 2026 21:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.

@msft-nathwania
msft-nathwania marked this pull request as ready for review July 13, 2026 23:11
radutta99 added a commit that referenced this pull request Jul 15, 2026
Hardware-validated fixes and API cleanup for ecc_sign_with_k (A4), plus a
throwaway on-device known-answer test. KAT PASSes on the EVB.

Production (ecc_sign_with_k, crypto/ecc.rs):
- Fix modular reduction: it is a DOUBLE-WIDTH PKA primitive, so stage xR
  into a zeroed 2*field buffer (xR || 0). The bare 48-byte input reduced
  xR || garbage and produced a wrong r; found by comparing to PR #511's
  run_ecdsa_on_engine.
- Zero-copy DMA I/O: take k/digest/d as &DmaBuf and r/s as &mut DmaBuf
  (caller-owned GSRAM), matching ecc_verify / ecdh_derive -- no operand or
  result copies; only internal scratch + the flash constants are staged.
- Take `curve` as a parameter and reject anything but P-384 with
  HsmError::UnsupportedCmd.
- Drop redundant zero-init: only xr_wide needs it (its zeroed high half is
  the reduction padding); every other scratch buffer is fully overwritten by
  its producing PKA op. Confirmed by the KAT.

Rename the PKA step primitives to the ecc_* prefix (matching PR #511):
ecc_mont_const_calc, ecc_point_mul, ecc_mod_reduction, ecc_mont_repr_in,
ecc_mont_repr_out, ecc_mod_inverse, ecc_mod_mul, ecc_mod_add -- engine.rs
definitions, opcode.rs doc, ecc.rs call sites.

Throwaway (to be removed once the cert-chain path is up):
- ecdsa_sign_self_test: NIST CAVP 186-3 P-384 SigGen KAT (same vector as
  #511), run once at boot from main.rs; logs PASS/FAIL over the trace UART.
- Enable trace-uart by default so the self-test result is visible on COM6.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11
wenbo-yuan pushed a commit to wenbo-yuan/azihsm-sdk that referenced this pull request Jul 20, 2026
* fw/uno: add P-384 modular PKA opcodes for deterministic ECDSA (A1)

Add the six P-384 UPKA modular / Montgomery opcode encodings
(ModReduction, MontReprIn, MontReprOut, ModInverse, ModMultiplication,
ModAddition) needed to build the firmware-side deterministic ECDSA sign
(RFC 6979) for the on-the-fly partition-id (PID) certificate leaf.

The PID leaf is signed with the P-384 alias key and regenerated lazily,
so its DER must be byte-stable; the sign is assembled from PKA modular
primitives (no firmware big-int) mirroring the mcr-hsm ECC-sign
self-test. Encodings mirror PkaCommandCode in mcr-hsm
cp/hsm/types/src/crypto/pka.rs. Only P-384 is required (alias key curve).

allow(dead_code) until the deterministic-sign wrappers (ecc_sign_with_k,
step A4) consume them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11

* fw/uno: add P-384 order/base-point constants + montgomery_size (A2)

Add the NIST P-384 curve order (ORDER384_LE), base point G x/y
(BASE384_X_LE / BASE384_Y_LE) in PKA little-endian operand order, plus a
montgomery_size(curve) helper (36/52/72 for P-256/384/521), to
fw/plat/uno/fw/pal/src/crypto/ecc.rs.

These feed the firmware-side deterministic ECDSA sign used to generate
the on-the-fly PID certificate leaf (curve order n modulus for the scalar
arithmetic; base point G for the k*G point-mul). Values are the
significant 48 little-endian bytes, matching PRIME384_LE, verified
against NIST secp384r1 and mirroring mcr-hsm ecc_constants.rs. P-384 only
(alias key curve).

allow(dead_code) until the deterministic-sign wrappers (ecc_sign_with_k,
step A4) consume them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11

* fw/uno: add PKA deterministic-sign step primitives to UpkaEngine (A3)

Architecture decision: build the deterministic ECDSA sign (cert-chain PID
leaf) with PAL-side orchestration — the PAL allocates all DMA buffers
from its per-IO scope and drives the sequence via with_engine — rather
than a single engine-side ecc_sign_with_k that would need ~11
intermediate buffers passed in. The DMA footprint is ~1 KB per sign out
of an 18 KB per-IO SRAM_IO_BUF (per-slot bump allocator, not a shared
pool), comfortable under 16 concurrent PKA engines + a 32-deep IO queue.

Add the public single-command primitives the PAL orchestrates on one held
engine (so a mont_const_calc's Montgomery state stays resident for the
ops that follow; execute_cmd does not wipe between commands):

- mont_const_calc(curve, modulus, mont_result)  [curve-generic]
- point_mul(curve, point_xy, scalar, result)    [curve-generic]
- mod_reduction / mont_repr_in / mont_repr_out / mod_inverse (unary)
- mod_multiplication / mod_addition (binary)

The six modular ops are P-384 only (the PID leaf is signed with the P-384
alias key); other curves are rejected via p384_modular. Being pub on the
re-exported UpkaEngine, they need no allow(dead_code); this also consumes
the A1 opcodes, so their allow(dead_code) attributes are dropped.

Supersedes the earlier "call execute_cmd directly" note: PAL orchestration
requires these named per-op methods so the opcodes stay encapsulated in
the driver.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11

* fw/uno: implement deterministic P-384 ECDSA sign orchestration (A4)

Add UnoHsmPal::ecc_sign_with_k(io, k, digest, d, r, s) — the PAL-side
orchestration (Arch 2) of the deterministic ECDSA-P384 sign used to
generate the on-the-fly cert-chain PID leaf. Because the leaf is
regenerated lazily its signature must be byte-stable, so k is supplied by
the caller (RFC 6979) rather than drawn from the PKA RNG.

Ports the mcr-hsm ECC-sign self-test step sequence (0-12): mont-const(p)
-> xR = (k*G).x -> mont-const(n) -> r = xR mod n -> mont_repr_in(k,r,e,d)
-> k^-1 -> s = k^-1*(e + r*d) mod n -> mont_repr_out(s). All operands are
PKA little-endian (48-byte field / 52-byte Montgomery).

Every operand/scratch buffer (~20, ~1 KB) is DMA-allocated from the per-IO
scope (the PKA engine DMAs its operands; a non-DMA source hardfaults), and
the whole sequence runs on ONE held engine via with_engine so the
Montgomery constant stays resident for the ops that follow. Results are
copied out and the secret-bearing scratch (k and everything derived from
k/d) is zeroized before scope release, which only rewinds a watermark.

Rejects bad operand lengths and degenerate r==0 / s==0. Consumes the A2
constants (ORDER384_LE, BASE384_X/Y_LE, montgomery_size), so their
allow(dead_code) attributes are dropped. allow(dead_code) on the method
itself until ecc_sign_deterministic (A6) / PID cert gen (B) consume it; it
is validated on-device by the KAT self-test (A7).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11

* fw/uno: generalize PKA modular-op primitives to all NIST curves

Open up the six UpkaEngine modular-op step primitives (mod_reduction,
mont_repr_in, mont_repr_out, mod_inverse, mod_multiplication,
mod_addition) from P-384-only to all three NIST curves (P-256/384/521),
matching the curve-generic mont_const_calc / point_mul and the rest of
the driver's ECC opcode surface.

- opcode.rs: add the P-256/P-521 encodings for each modular op (18 consts
  total) and per-op *_opcode(curve) selectors, mirroring the existing
  mont_const_calc_opcode / ecc_sign_opcode pattern.
- engine.rs: drop the p384_modular gate; the six methods now resolve their
  opcode via the selectors and accept any supported curve.

The deterministic cert-chain PID-leaf sign still only exercises P-384 (the
alias key curve), but the primitives are no longer artificially
restricted — a general-purpose PKA driver should expose its modular ops
for any curve.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11

* fw/uno: finalize deterministic P-384 ECDSA sign + KAT self-test

Hardware-validated fixes and API cleanup for ecc_sign_with_k (A4), plus a
throwaway on-device known-answer test. KAT PASSes on the EVB.

Production (ecc_sign_with_k, crypto/ecc.rs):
- Fix modular reduction: it is a DOUBLE-WIDTH PKA primitive, so stage xR
  into a zeroed 2*field buffer (xR || 0). The bare 48-byte input reduced
  xR || garbage and produced a wrong r; found by comparing to PR Azure#511's
  run_ecdsa_on_engine.
- Zero-copy DMA I/O: take k/digest/d as &DmaBuf and r/s as &mut DmaBuf
  (caller-owned GSRAM), matching ecc_verify / ecdh_derive -- no operand or
  result copies; only internal scratch + the flash constants are staged.
- Take `curve` as a parameter and reject anything but P-384 with
  HsmError::UnsupportedCmd.
- Drop redundant zero-init: only xr_wide needs it (its zeroed high half is
  the reduction padding); every other scratch buffer is fully overwritten by
  its producing PKA op. Confirmed by the KAT.

Rename the PKA step primitives to the ecc_* prefix (matching PR Azure#511):
ecc_mont_const_calc, ecc_point_mul, ecc_mod_reduction, ecc_mont_repr_in,
ecc_mont_repr_out, ecc_mod_inverse, ecc_mod_mul, ecc_mod_add -- engine.rs
definitions, opcode.rs doc, ecc.rs call sites.

Throwaway (to be removed once the cert-chain path is up):
- ecdsa_sign_self_test: NIST CAVP 186-3 P-384 SigGen KAT (same vector as
  Azure#511), run once at boot from main.rs; logs PASS/FAIL over the trace UART.
- Enable trace-uart by default so the self-test result is visible on COM6.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11

* fw/uno: add RFC 6979 deterministic-k derivation for P-384 ECDSA (A5)

The on-the-fly PID cert leaf is regenerated lazily, so its ECDSA
signature must be byte-stable. Draw the per-message secret `k`
deterministically (RFC 6979 §3.2, HMAC-SHA384 DRBG over the private
key and message hash) instead of from a PRNG, so `ecc_sign_with_k`
(A4) produces a reproducible signature.

`ecc_generate_k_rfc6979` (P-384 only) reuses the PAL HMAC-SHA384
one-shot. RFC 6979's integer/octet conversions are big-endian while
PAL operands are little-endian, so `d`/`digest` are byte-reversed on
input and `k` on output. Since hlen == qlen == 384 and n > 2^383,
each DRBG block yields one candidate and `bits2octets(digest)`
reduces mod n with a single conditional subtraction. DRBG state and
private-key-derived scratch are volatile-scrubbed before return.

Verified against the RFC 6979 Appendix A.2.6 P-384 / SHA-384
"sample" vector via a throwaway boot self-test:

  RFC6979 P384 k self-test: PASS

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11

* fw/uno: compose RFC 6979 deterministic ECDSA-P384 sign (A6/A7)

Add `ecc_sign_deterministic`: derive the per-message secret `k`
(RFC 6979, A5) and produce `(r, s)` via the PKA sign (A4) on one
held engine. This is the byte-stable signer the lazily regenerated
PID cert leaf needs.

Refactor the RFC 6979 DRBG out of `ecc_generate_k_rfc6979` into
shared `rfc6979_seed` / `rfc6979_generate` / `rfc6979_reseed` steps
(plus an `Rfc6979Drbg` state) so the k-only path (A5) and the sign
path reuse one implementation. `ecc_sign_deterministic` drives the
full RFC 6979 §3.2 loop: on the ~2^-384 degenerate result
(`r == 0` / `s == 0`) it advances the DRBG to the next candidate
rather than looping on the same k. A bounded try count guarantees
termination; DRBG/private-key scratch is volatile-scrubbed.

Verified end-to-end against the RFC 6979 Appendix A.2.6 P-384 /
SHA-384 "sample" vector (known r,s) via a throwaway boot self-test
(satisfies A7):

  ECDSA P384 KAT self-test: PASS
  RFC6979 P384 k self-test: PASS
  RFC6979 P384 deterministic-sign self-test: PASS

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cfbdfe3b-9b89-4ff3-9657-57d6cc0d3c11

* fw/uno: split deterministic ECDSA-P384 into crypto/ecc_det.rs

Move the RFC 6979 deterministic-sign path out of the ~1080-line
crypto/ecc.rs into a dedicated crypto/ecc_det.rs, so the base HsmEcc
trait adapter stays focused on the standard curve operations. The new
module will also host deterministic ECC key generation later.

Moved: the P-384 order/base-point constants, montgomery_size,
be_sub_assign, secure_zero, the RFC 6979 HMAC-SHA384 DRBG, and the
ecc_sign_with_k / ecc_generate_k_rfc6979 / ecc_sign_deterministic
methods (plus the throwaway KAT self-tests). PRIME384_LE is shared with
the base HsmEcc impl, so it stays in ecc.rs and is now pub(super).

No functional change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c

* fw/uno: make RFC 6979 DRBG DMA-resident and zero-copy (P-384)

The RFC 6979 HMAC-SHA384 DRBG staged its K/V state through fresh DMA
allocations and stack copies on every HMAC (~5x per deterministic sign,
via hmac_sha384_bytes), plus byte-wise reversal loops. With up to 128
concurrent signs this is needless allocation/copy churn (the SHA engine
is the real serialization point).

Rework the DRBG to hold K, V, a tag slot, and the input-assembly buffer
as persistent DMA scratch allocated once per sign. Each step runs the
HMAC in place and rotates v/tag (or key/tag) by reference with
core::mem::swap -- zero-copy. This drops per-sign DMA allocs from ~15 to
~5 and copies from ~15 to ~2, and removes the nested per-HMAC scope.

Also: replace the byte-wise reversal loops with copy_from_slice +
slice::reverse(), and drop the hand-rolled secure_zero in favour of
DmaBuf::zeroize on the DRBG buffers. ecc_sign_with_k (the PKA sequence)
is unchanged.

Public API of ecc_generate_k_rfc6979 / ecc_sign_deterministic is
unchanged. Validated on Uno hardware: all three boot KATs pass
(ECDSA P384 KAT, RFC6979 P384 k, RFC6979 P384 deterministic-sign)
against the RFC 6979 A.2.6 vectors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c

* fw/uno: drop throwaway boot KAT self-tests from deterministic ECDSA

The RFC 6979 / ECDSA-P384 deterministic sign was validated on Uno
hardware (all three boot KATs pass against the RFC 6979 A.2.6 and NIST
CAVP P-384 vectors). Remove the throwaway on-boot self-test harness so
this PR ships only production code:

- the three ecc_det.rs `*_self_test` methods,
- their boot-time wiring in app/main.rs (plus the now-unused `error`
  tracing import),
- the `trace-uart` default toggle in app/Cargo.toml (back to
  `default = []`, matching main).

The deterministic-sign API (ecc_sign_with_k / ecc_generate_k_rfc6979 /
ecc_sign_deterministic) stays as the public entry points for the
follow-up cert-chain (PID leaf) work and keeps its #[allow(dead_code)]
until that consumer lands.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c

* fw/uno: rustfmt — wrap multi-arg PKA/HMAC calls to CI style

CI's nightly `cargo fmt --check` wraps the multi-argument
`execute_cmd(...)` calls in upka/engine.rs and the `rfc6979_update_*` /
`hmac_sign(...)` calls in ecc_det.rs one-arg-per-line, and trims trailing
whitespace in a doc comment. Apply those. The `use` ordering is left as
CI expects (its nightly honors the repo's unstable group_imports /
imports_granularity options, which local rustfmt rejects).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c

* fw/uno: scrub deterministic-ECDSA secrets on all exit paths

Address PR review: the secret-scrub in the deterministic sign path only
ran on the success path, so an early `?` (PKA/HMAC hardware error) left
k/d-derived material resident in the scoped DMA heap — the scoped
allocator only rewinds its watermark on release, it does not clear freed
DMA.

- ecc_sign_with_k: capture the PKA-sequence result, then zeroize the
  internal scratch unconditionally (now also wiping xr/xr_wide, which
  hold (k*G).x -- public as r, but k-derived -- as defense-in-depth).
- ecc_generate_k_rfc6979 / ecc_sign_deterministic: run the fallible
  derive/sign in an inner async block, then scrub the DRBG state (and k,
  for the sign) unconditionally afterward.

Success-path behaviour and the (r,s)/k outputs are unchanged; only the
error / early-return paths now scrub, so the on-hardware KAT result is
unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c

* fw/uno: constant-time RFC 6979 candidate-range check

Address PR review: the `1 <= k < n` acceptance test on the RFC 6979 DRBG
output used short-circuiting comparisons (`iter().any` + slice `<`), which
are not constant-time. The candidate is the secret ECDSA per-message
nonce, so byte-position-dependent timing could leak nonce bits (cf. the
Minerva / TPM-Fail ECDSA nonce-leak attacks that recover the key from a
few biased/leaked nonce bits).

Add `ct_in_range`, which reads every byte with no data-dependent early
exit (OR-accumulate for `!= 0`; full-width big-endian subtraction for
`< n`), and use it in both ecc_generate_k_rfc6979 and
ecc_sign_deterministic. Same boolean result, no timing dependence on the
secret candidate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c

* fw/uno: validate PKA step-primitive buffer sizes; fix nonce-retry comment

Address PR review:

- upka/engine.rs: the eight new ECC modular/Montgomery step primitives
  (ecc_mont_const_calc / ecc_point_mul / ecc_mod_reduction /
  ecc_mont_repr_in / ecc_mont_repr_out / ecc_mod_inverse / ecc_mod_mul /
  ecc_mod_add) now validate operand/result buffer lengths with
  `ensure_cmd_input` before issuing the PKA DMA command, mirroring
  ecc_verify/ecdh_derive. This prevents out-of-bounds DMA reads/writes on
  undersized buffers. Adds a `mont_operand_size(curve)` helper (36/52/72)
  for the wider Montgomery-domain operands.

- ecc_det.rs: correct the RFC6979_MAX_TRIES doc comment. For P-384 the
  dominant reseed cause is the range check (candidate >= n, ~2^-194),
  not the r/s == 0 degenerate-signature case (~2^-384).

Widths were checked against the working P-384 sign buffers, so the
success-path behaviour and on-hardware KAT result are unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c

* fw/uno: allow clippy::too_many_arguments on ecc_sign_with_k

ecc_sign_with_k follows the zero-copy DMA convention (each operand is a
separate caller-provided DmaBuf), so it takes 8 arguments and trips
clippy::too_many_arguments -- the pal crate does not inherit the
workspace-level allow. Add the per-function allow, matching the existing
convention in kdf.rs / gcm.rs / aes.rs in the same crate.

CI clippy only surfaced this now that the fmt step passes (the precheck
runs fmt before clippy).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c

* fw/uno: precompute P-384 order big-endian; drop per-call reverse

The RFC 6979 candidate-range check compares big-endian (the DRBG emits
candidates big-endian per bits2int), so ecc_generate_k_rfc6979 and
ecc_sign_deterministic reversed ORDER384_LE into a local n_be on every
call. Precompute ORDER384_BE from ORDER384_LE at compile time (const
reverse48) and use it directly, removing the runtime reverse from both
sign entry points. Behavior is unchanged (ORDER384_BE is byte-identical
to the previous runtime result, verified against the published P-384
order n).

Addresses PR review feedback on the redundant runtime reverse.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a04329e

* fw/uno: dedupe Montgomery-size table; reconcile mont_const scratch width

Two PR review follow-ups on the deterministic ECDSA-P384 path:

- montgomery_size in ecc_det.rs duplicated the driver's mont_operand_size
  (both 36/52/72), risking silent divergence. The driver helper was only
  pub(crate); make it pub, re-export it from the upka crate, and use it in
  ecc_det.rs — single source of truth for the Montgomery operand widths.

- ecc_mont_const_calc validated mont_result against mont_operand_size (52B
  for P-384), while ecc_verify/ecdh_derive issue the same mont_const_calc
  opcode with a hsm_point_size (48B) scratch buffer. The Montgomery constant
  is a value < modulus, so it fits the point width; the reference firmware
  keeps it engine-internal. Relax the check to hsm_point_size so all callers
  of the opcode validate consistently. Behavior is unchanged: the sign path
  still passes a 52B buffer, which satisfies the >= 48B check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a04329e

* fw/uno: rustfmt import order (UpkaEccCurve before mont_operand_size)

CI cargo +nightly fmt --check sorts same-crate item imports
case-sensitively (uppercase terminal before lowercase), matching the
existing ecc.rs ordering. Move the new mont_operand_size import after
UpkaEccCurve.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a04329e

* fw/uno: guard RFC 6979 update_key slice against out-of-range len

rfc6979_update_key used drbg.msg.split_at(len), which panics if len
exceeds drbg.msg.len(). Current callers pass constant lengths bounded by
RFC6979_SEED_MSG_LEN, so it never trips today, but a panic is an
avoidable abort path in crypto code. Range-check len up front and return
HsmError::InvalidArg instead. Behavior is unchanged for the in-range
callers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a04329e

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 20, 2026 23:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings August 1, 2026 00:46
@msft-nathwania
msft-nathwania force-pushed the user/v-nathwania/uno_selftest branch from 39d0b9d to ff7a0a4 Compare August 1, 2026 00:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants